package com.demo.task.utils

import android.util.Log
import com.demo.task.download.DownloadManager
import com.demo.task.download.addDownloadListener
import com.demo.task.ext.MsgType
import com.demo.task.ext.getOrPutHashSet
import com.demo.task.ext.getResPath
import com.demo.task.ext.getUrlFileName
import com.demo.task.ext.sendError
import com.demo.task.socket.RemoteInterface
import com.google.gson.Gson
import com.orhanobut.logger.Logger
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import java.io.File
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class DownloadUtil @Inject constructor(
    private val socketIO: RemoteInterface
) {

    private val jobs by lazy { mutableMapOf<String, MutableList<Job>>() }
    private val gson by lazy { Gson() }

    private val downloadManager by lazy {
        DownloadManager.Builder()
            .savePath(getResPath())
            .maxRetryTimes(60)
            .build()
    }

    private val _downProgress = MutableStateFlow("")
    val downProgress: StateFlow<String> = _downProgress


    private var currentProg = 0 //当前进度
    private var totalProg = 0//总进度

    init {
        downloadManager.addDownloadListener(
            begin = {
                currentProg++
            },
            prog = {
                runBlocking {
                    val totalNum = downloadManager.total
                    _downProgress.emit("Downloading：${totalNum}/$currentProg $it%")
                    val total = (((currentProg - 1) + (it / 100.0)) / totalNum * 100).toInt()
                    if (totalProg != total) {
                        totalProg = total
                        sendProgress(total)
                    }
                }
            },
            fileComplete = { url ->
                val fileName = url.getUrlFileName()
                jobs[fileName]?.forEach {
                    it.cancel()
                }
            },
            complete = {
                runBlocking {
                    _downProgress.emit("Download completed")
                    launch {
                        delay(3_000)
                        _downProgress.emit("")
                        sendProgress(100)
                    }
                }
                currentProg = 0
                totalProg = 0
            },
            failure = { url, t ->
                socketIO.sendError(MsgType.DOWNLOAD.name, url, t.message)
            }
        )
    }

    private fun sendProgress(progress: Int) {
        val message = mapOf(
            "type" to "basic",
            "message" to mapOf("sync" to progress)
        )
        socketIO.send(gson.toJson(message))
    }

    fun download(urls: List<String>) {
        downloadManager.download(urls)
    }

    fun download(url: String) {
        downloadManager.download(url)
    }

    suspend fun awaitFile(path: String) {
        val file = File(path)
        awaitFile(file)
    }

    suspend fun awaitFile(file: File, notExist: () -> Unit = {}) {
        if (!file.exists()) {
            notExist.invoke()
            withContext(Dispatchers.Default) {
                val job = launch {
                    Log.i(TAG, "awaitFile: 等待文件 ${file.name}")
                    Logger.i("awaitFile: 等待文件 ${file.name}")
                    awaitCancellation()
                }
                jobs.getOrPutHashSet(file.name).add(job)
                job.join()
            }
        }
    }

    fun clearAll() {
        jobs.forEach {
            it.value.forEach {
                it.cancel()
            }
        }
    }

    fun release() {
        clearAll()
        downloadManager.release()
    }

    companion object {
        private const val TAG = "DownloadUtil"
    }
}

